﻿using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using ThresholdAssistant.Attributes.Extensions;
using ThresholdAssistant.Http.Extensions;
using ThresholdAssistant.NLogSdk;
using ThresholdAssistant.Tools;
using ThresholdAssistant.Tools.Extensions;

namespace ThresholdAssistant.Attributes.Middlewares
{
    /// <summary>
    /// 接口日志收集中间件
    /// </summary>
    public class NLogMiddleware
    {
        /// <summary>
        /// 请求管道
        /// </summary>
        private readonly RequestDelegate _next;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        public NLogMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 中间件执行方法
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            var beginTime = DateTime.Now.ToCstTime();//开始时间

            var url = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";

            string requestBodyStr = "";
            string responseBodyStr = "";
            string serialNo = "";
            //string robotSerialNo = "";
            //string merchantNo = "";
            try
            {
                 requestBodyStr = await context.Request.GetRequestBody();//请求参数
                 responseBodyStr = await GetResponseBodyJsonStr(context);//响应结果
                var paramters = ConvertObject(requestBodyStr, responseBodyStr);//转换对象
                 //robotSerialNo = GetRobotSerialNo(paramters.request, context.Request.Headers);//机器人编号
                 serialNo = GetSerialNo(context.Request.Headers, paramters.request, paramters.response);//流水编号
                 //merchantNo = paramters.request.GetPropertyValue<string>("merchant_no", "MerchantNo", "vcMerchantNo"); //商家编号
              

                var path = context.Request.Path;
                if (!path.HasValue || path.Value.IndexOf("swagger") > 0 || path.Value.IndexOf("api-docs") > 0)
                {
                    //非空校验
                    await _next(context);
                    return;
                }

                var exclActionList = NLogMiddlewareExtensions.ExclActionList;
                var host = ($"{context.Request.Host}" ?? "").TrimStart('/').TrimEnd('/');
                if (exclActionList != null && !string.IsNullOrEmpty(url) && exclActionList.Any(e => url.Contains(e)))
                {
                    //拦截一些特定日志
                    return;
                }

                context.Response.OnCompleted(async () =>
                {
                    await Task.CompletedTask;
                    if (NLogMiddlewareExtensions.Level != null)
                    {
                        NLogUtil.SaveLog(NLogMiddlewareExtensions.Level, new NLogModel()
                        {
                            Url = url,
                            ReqId = serialNo,
                            SerialNo = serialNo,
                            //MerchantNo = merchantNo,
                            //RobotSerialNo = robotSerialNo,
                            Request = requestBodyStr.JsonCompressAndEscape(),
                            Response = responseBodyStr.JsonCompressAndEscape(),
                            Headers = context.Request.Headers.JsonCompressAndEscape(),
                            Type = NLogMiddlewareExtensions.IsSaveContent ? "All" : "",
                            TotalSeconds = (DateTime.Now.ToCstTime() - beginTime).TotalSeconds,
                            Content = NLogMiddlewareExtensions.IsSaveContent ? $"【头部信息】{JsonConvert.SerializeObject(context.Request.Headers)}，【Body参数】:{requestBodyStr}，【返回结果】:{responseBodyStr.JsonEscape()}" : ""
                        });
                    }
                    else
                    {
                        NLogUtil.Info(new NLogModel()
                        {
                            Url = url,
                            ReqId = serialNo,
                            SerialNo = serialNo,
                            //MerchantNo = merchantNo,
                            //RobotSerialNo = robotSerialNo,
                            Request = requestBodyStr.JsonCompressAndEscape(),
                            Response = responseBodyStr.JsonCompressAndEscape(),
                            Headers = context.Request.Headers.JsonCompressAndEscape(),
                            Type = NLogMiddlewareExtensions.IsSaveContent ? "All" : "",
                            TotalSeconds = (DateTime.Now.ToCstTime() - beginTime).TotalSeconds,
                            Content = NLogMiddlewareExtensions.IsSaveContent ? $"【头部信息】{JsonConvert.SerializeObject(context.Request.Headers)}，【Body参数】:{requestBodyStr}，【返回结果】:{responseBodyStr.JsonEscape()}" : ""
                        });
                    }
                });
            }
            catch (Exception ex)
            {
                NLogUtil.Error(new NLogModel()
                {
                    Url = url,
                    ReqId = serialNo,
                    Response = $"{ex}",
                    SerialNo = serialNo,
                    //MerchantNo = merchantNo,
                    //RobotSerialNo = robotSerialNo,
                    Request = requestBodyStr.JsonCompressAndEscape(),
                    Headers = context.Request.Headers.JsonCompressAndEscape(),
                    Type = NLogMiddlewareExtensions.IsSaveContent ? "Error" : "",
                    TotalSeconds = (DateTime.Now.ToCstTime() - beginTime).TotalSeconds,
                    Content = NLogMiddlewareExtensions.IsSaveContent ? $"【头部信息】{JsonConvert.SerializeObject(context.Request.Headers)}，【Body参数】:{requestBodyStr}，【错误信息】:{ex}" : ""
                });
            }
        }

        /// <summary>
        /// 获取返回结果的JSON字符串
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task<string> GetResponseBodyJsonStr(HttpContext context)
        {
            var responseBodyString = "";
            var originalBodyStream = context.Response.Body;
            using (var responseBody = new MemoryStream())
            {
                context.Response.Body = responseBody;
                await _next(context);
                responseBodyString = await context.Response.GetResponseBody();
                await responseBody.CopyToAsync(originalBodyStream);
            }
            return responseBodyString;
        }

        /// <summary>
        /// 转换对象
        /// </summary>
        /// <param name="requestJson">请求参数</param>
        /// <param name="responseJson">返回结果</param>
        /// <returns></returns>
        private static (object request, object response) ConvertObject(string requestJson, string responseJson)
        {
            object request = null;
            object response = null;
            try
            {
                request = JsonConvert.DeserializeObject<object>(requestJson);
            }
            catch (Exception)
            {

            }

            try
            {
                response = JsonConvert.DeserializeObject<object>(responseJson);
            }
            catch (Exception)
            {

            }
            return (request, response);
        }

        /// <summary>
        /// 获取机器人编号
        /// </summary>
        /// <param name="request">请求参数</param>
        /// <param name="headers">头部信息</param>
        /// <returns></returns>
        private static string GetRobotSerialNo(object request, IHeaderDictionary headers)
        {
            var robotSerialNo = request.GetPropertyValue<string>("robot_serial_no", "RobotSerialNo", "vcRobotSerialNo", "user_serial_no");
            if (string.IsNullOrEmpty(robotSerialNo))
            {
                if (headers != null && headers.Any())
                {
                    if (string.IsNullOrEmpty(robotSerialNo) && headers.ContainsKey("user-serial-no"))
                    {
                        robotSerialNo = $"{headers["user-serial-no"]}";
                    }

                    if (string.IsNullOrEmpty(robotSerialNo) && headers.ContainsKey("robot_wx_id"))
                    {
                        robotSerialNo = $"{headers["robot_wx_id"]}".ToSerialNo();
                    }

                    if (string.IsNullOrEmpty(robotSerialNo) && headers.ContainsKey("robot-id"))
                    {
                        robotSerialNo = $"{headers["robot-id"]}".ToSerialNo();
                    }

                    if (string.IsNullOrEmpty(robotSerialNo) && headers.ContainsKey("RobotWxId"))
                    {
                        robotSerialNo = $"{headers["RobotWxId"]}".ToSerialNo();
                    }

                    if (string.IsNullOrEmpty(robotSerialNo) && headers.ContainsKey("vcRobotWxId"))
                    {
                        robotSerialNo = $"{headers["vcRobotWxId"]}".ToSerialNo();
                    }
                }

                var robotId = request.GetPropertyValue<string>("robot_wx_id", "robot_id", "RobotWxId", "vcRobotWxId");
                if (!string.IsNullOrEmpty(robotId) && string.IsNullOrEmpty(robotSerialNo))
                {
                    //转换成机器人编号
                    robotSerialNo = robotId.ToSerialNo();
                }
            }
            return robotSerialNo;
        }

        /// <summary>
        /// 获取业务流水号
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="request">请求参数</param>
        /// <param name="response">返回结果的Json字符串</param>
        /// <returns></returns>
        private static string GetSerialNo(IHeaderDictionary headers, object request, object response)
        {
            var serialNo = string.Empty;
            if (headers.ContainsKey("x-traceId-header"))
            {
                serialNo = headers["x-traceId-header"];
            }

            if (string.IsNullOrEmpty(serialNo))
            {
                serialNo = request.GetPropertyValue<string>("serial_no", "SerialNo", "vcSerialNo");
                if (string.IsNullOrEmpty(serialNo))
                {
                    serialNo = response.GetPropertyValue<string>("serial_no", "SerialNo", "vcSerialNo");
                }
            }

            if (string.IsNullOrEmpty(serialNo))
            {
                //默认给一个新的流水编号
                serialNo = SnowflakeNoUtil.GetSerialNo();
            }

            if (!string.IsNullOrEmpty(serialNo))
            {
                if (!headers.ContainsKey("x-traceId-header"))
                {
                    headers.Add("x-traceId-header", serialNo);
                }
                else
                {
                    headers["x-traceId-header"] = serialNo;
                }
            }
            return serialNo;
        }
    }
}